/*
 * NetworkHelper.cpp
 *
 *  Created on: 2013年7月26日
 *      Author: sun
 */

#include "Socket.h"
#include "../common/CToolKit.h"
#include "../common/Log.h"
#include "../common/common_define.h"

namespace sdfs {

Socket::Socket(ServerConfig *conf, bool isBlock, bool isKeepalive)
{
	m_sBody = new SocketBody;
	memset(m_sBody, 0, sizeof(SocketBody));
	m_sBody->bBlock = isBlock;
	m_sBody->sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(m_sBody->sockfd < 0)
	{
		m_sBody->status = CREATEFAILURE;
		return;
	}
	int sock_reuse = 1;
	int err = setsockopt(m_sBody->sockfd,SOL_SOCKET,SO_REUSEADDR,(char *)&sock_reuse,sizeof(sock_reuse));
	if( err != 0 )
	{
		m_sBody->status = CREATEFAILURE;
		return;
	}
	if(isKeepalive)
	{
		int KeepAlive = 1;
		err = setsockopt(m_sBody->sockfd,SOL_SOCKET,
				SO_KEEPALIVE,(char *)&KeepAlive, sizeof(int));
		if( err != 0 )
		{
			m_sBody->status = CREATEFAILURE;
			return;
		}
		m_sBody->bKeepAlive = true;
	}
	m_sBody->status = CLOSED;
	m_sBody->config = conf;
}

Socket::Socket(int sockfd, SocketStatus status, bool isblock)
{
	this->m_sBody = new SocketBody;
	m_sBody->bBlock = isblock;
	m_sBody->sockfd = sockfd;
	m_sBody->bBlock = isblock;
	m_sBody->bKeepAlive = false;
	if(!isblock)
		CToolKit::setNoblock(sockfd);
	m_sBody->status = status;
}

Socket *Socket::BuildConnectedSocket(int fd, bool isBlock)
{
	return new Socket(fd, ESTABLISHED, isBlock);
}

Socket::~Socket() {
	//Close();
	Log::Debug("delete fd:%d", m_sBody->sockfd);
	delete m_sBody;
}

int Socket::CreateListenSocket()
{
	Log::Debug("CreateListenSocket: %d", m_sBody->sockfd);
	if(m_sBody->status != CLOSED || m_sBody->status == CREATEFAILURE)
	{
		Log::Warning("socket: %d has already opened, please close first", m_sBody->sockfd);
		return -1;
	}
	int result;
	memset(&m_sBody->s_addr, 0, sizeof(struct sockaddr_in));
	m_sBody->s_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	m_sBody->s_addr.sin_family = AF_INET;
	m_sBody->s_addr.sin_port = htons(m_sBody->config->nPort);
	int len = sizeof(struct sockaddr);
	result = bind(m_sBody->sockfd, (struct sockaddr *)&m_sBody->s_addr, len);
	if(result != 0)
	{
		Log::Error("file :"__FILE__", line: %d,"
				"call bind, "
				"errno:%d, info: %s, bind", __LINE__,
				result, STRERROR(result));
		return -1;
	}
	result = listen(m_sBody->sockfd, LISTEN_QUEUE);
	if(result != 0)
	{
		Log::Error("file :"__FILE__", line: %d,"
				"call listen, "
				"errno:%d, info: %s, bind", __LINE__,
				result, STRERROR(result));
		return -1;
	}
	m_sBody->status = LISTENING;
	return m_sBody->sockfd;
}

int Socket::Connect()
{
	Log::Debug("CreateConnectSocket: %d", m_sBody->sockfd);
	if(m_sBody->status != CLOSED)
	{
		Log::Warning("socket: %d has already opened, please close first", m_sBody->sockfd);
		return -1;
	}
	if(m_sBody->status == CREATEFAILURE)
	{
		Log::Warning("socket: %d did not create successful", m_sBody->sockfd);
		return -1;
	}
	int result;
	memset(&m_sBody->s_addr, 0, sizeof(struct sockaddr_in));
	m_sBody->s_addr.sin_family = AF_INET;
	m_sBody->s_addr.sin_port = htons(m_sBody->config->nPort);
	Log::Debug("fd:%d connecting ip: %s:%d", m_sBody->sockfd, m_sBody->config->strIp, m_sBody->config->nPort);
	result = inet_pton(AF_INET, m_sBody->config->strIp, &(m_sBody->s_addr.sin_addr));
	if(result != 1)
	{
		Log::Error("file :"__FILE__", line: %d,"
					"call inet_pton, "
					"errno:%d, info: %s", __LINE__,
					result, STRERROR(result));
	}
	if((result = connect(m_sBody->sockfd,(struct sockaddr*)&(m_sBody->s_addr),sizeof(struct sockaddr)))<0)
	{
		Log::Error("file :"__FILE__", line: %d,"
					"call connect, "
					"errno:%d, info: %s", __LINE__,
					errno, STRERROR(errno));
		return -1;
	}
	if(!m_sBody->bBlock)
	{
		result = CToolKit::setNoblock(m_sBody->sockfd);
		if(result < 0)
		{
			Log::Error("file :"__FILE__", line: %d,"
					"call setNoblock, "
					"errno:%d, info: %s, bind", __LINE__,
					result, STRERROR(result));
		}
	}
	m_sBody->status = ESTABLISHED;
	return m_sBody->sockfd;
}

int Socket::DisConnect()
{
	if(m_sBody->status == CLOSED)
	{
		Log::Warning("socket: %d has already closed", m_sBody->sockfd);
		return -1;
	}
	shutdown(m_sBody->sockfd, SHUT_RDWR);
	int result = close(m_sBody->sockfd);
	if(result != 0)
	{
		Log::Error("file :"__FILE__", line: %d,"
					"call close, "
					"errno:%d, info: %s, bind", __LINE__,
					errno, STRERROR(errno));
		return -1;
	}
	m_sBody->status = CLOSED;
	return 0;
}

Socket *Socket::Clone()
{
	return new Socket(m_sBody->config, m_sBody->bBlock, m_sBody->bKeepAlive);
}

int Socket::Read(void* buf, int size)
{
	return CToolKit::Readn(m_sBody->sockfd, buf, size);
}

int Socket::Write(const void* buf, int size)
{
	return CToolKit::Writen(m_sBody->sockfd, buf, size);
}

int Socket::SetNoBlock(bool flag)
{
	if(flag)
		return CToolKit::setNoblock(m_sBody->sockfd);
	return CToolKit::setBlock(m_sBody->sockfd);
}

int Socket::GetDescriptor()
{
	return m_sBody->sockfd;
}

const char* Socket::GetServerName()
{
	return m_sBody->config->strName;
}


SocketStatus Socket::GetStatus()
{
	return m_sBody->status;
}

} /* namespace sdfs */
